﻿using Abp.Application.Services.Dto;
using Abp.Auditing;
using Abp.Authorization;
using Abp.Authorization.Users;
using Abp.Domain.Repositories;
using Abp.Linq.Extensions;
using Abp.Localization;
using Abp.Runtime.Session;
using AbpProjectTemplate.Application.Users.Dtos;
using AbpProjectTemplate.Application.Users.Filters;
using AbpProjectTemplate.Components.Auditing;
using AbpProjectTemplate.Components.Authorization;
using AbpProjectTemplate.Components.Identity;
using AbpProjectTemplate.Domain.Users;
using AbpProjectTemplate.Extensions;
using Microsoft.AspNetCore.Identity;
using System.Linq;
using System.Threading.Tasks;

namespace AbpProjectTemplate.Application.Users
{
    /// <summary>
    /// 用户
    /// </summary>
    [AppAuditTag("用户")]
    public class UserAppService : ApplicationService
    {
        private readonly UserManager _userManager;
        private readonly LogInManager _logInManager;
        private readonly IPasswordHasher<User> _passwordHasher;
        private readonly IRepository<UserLoginAttempt, long> _userLoginAttemptRepository;
        private readonly IRepository<AuditLog, long> _auditLogRepository;
        private readonly IRepository<User, long> _userRepository;

        public UserAppService(
            UserManager userManager,
            LogInManager logInManager,
            IPasswordHasher<User> passwordHasher,
            IRepository<AuditLog, long> auditLogRepository,
            IRepository<UserLoginAttempt, long> userLoginAttemptRepository,
            IRepository<User, long> userRepository)
        {
            this._userManager = userManager;
            this._logInManager = logInManager;
            this._passwordHasher = passwordHasher;
            this._auditLogRepository = auditLogRepository;
            this._userLoginAttemptRepository = userLoginAttemptRepository;
            this._userRepository = userRepository;
        }
        /// <summary>
        /// 设置用户语言
        /// </summary>
        [AppAudited("设置语言")]
        [AbpAuthorize]
        public async Task ChangeLanguage(UserChangeLanguageInput input)
        {
            await this.SettingManager.ChangeSettingForUserAsync(
                this.AbpSession.ToUserIdentifier(),
                LocalizationSettingNames.DefaultLanguage,
                input.LanguageName
            );
        }
        /// <summary>
        /// 修改密码
        /// </summary>
        [AppAudited("修改密码")]
        [AbpAuthorize]
        public async Task ChangePassword(UserChangePasswordInput input)
        {
            var user = await this._userManager.GetUserByIdAsync(this.CurrentUserId);
            var loginAsync = await this._logInManager.LoginAsync(user.UserName, input.CurrentPassword, shouldLockout: false);
            if (loginAsync.Result != AbpLoginResultType.Success)
            {
                throw new AppUserFriendlyException("Your 'Existing Password' did not match the one on record.  Please try again or contact an administrator for assistance in resetting your password.");
            }
            var result = await this._userManager.ChangePasswordAsync(user, input.NewPassword);
            if (!result.Succeeded)
                throw new AppUserFriendlyException(result.Errors.Select(t => t.Description).FirstOrDefault());
        }

        /// <summary>
        /// 获取用户登录日志
        /// </summary>
        [AppAuthorize(AppPermissions.System.LoginLogs)]
        public async Task<PagedResultDto<UserLoginAttempt>> GetLoginLogs(UserGetLoginLogsFilter filter)
        {
            long.TryParse(filter.Keywords, out var id);

            var query = this._userLoginAttemptRepository.GetAll()
                .WhereIf(filter.IsSuccess == true, t => t.UserId.HasValue)
                .WhereIf(filter.IsSuccess == false, t => !t.UserId.HasValue)
                .WhereIf(id > 0, t => t.UserId == id)
                .WhereIf(id <= 0 && filter.Keywords.HasValue(), t => t.UserNameOrEmailAddress.Contains(filter.Keywords));
            if (filter.UserType.HasValue)
            {
                var query1 = this._userRepository.GetAll().Where(t => t.UserType == filter.UserType);
                query = query.Join(query1, a => a.UserId, b => b.Id, (a, b) => a);
            }
            filter.SetDefaultSortIfNot("Id");
            return await this.CreatePagedResultAsync(query, filter);
        }
        /// <summary>
        /// 获取用户审计日志
        /// </summary>
        [AppAuthorize(AppPermissions.System.HandleLogs)]
        public async Task<PagedResultDto<AuditLogInfo>> GetAuditLogs(UserGetAuditLogsFilter filter)
        {
            long.TryParse(filter.Keywords, out var id);
            var queryUser = this._userRepository.GetAll()
                .WhereIf(filter.UserType.HasValue, t => t.UserType == filter.UserType)
                ;
            var query = this._auditLogRepository.GetAll()
                 .WhereIf(id > 0, t => t.UserId == id)
                 .WhereIf(id <= 0 && filter.Keywords.HasValue(), t => t.CustomData.Contains(filter.Keywords));

            var qr = from q in query
                     join a in queryUser on q.UserId equals a.Id
                     into ag
                     from a2 in ag.DefaultIfEmpty()
                     select new AuditLogInfo
                     {
                         BrowserInfo = q.BrowserInfo,
                         ClientIpAddress = q.ClientIpAddress,
                         ClientName = q.ClientName,
                         CustomData = q.CustomData,
                         Exception = q.Exception,
                         ExecutionDuration = q.ExecutionDuration,
                         ExecutionTime = q.ExecutionTime,
                         Id = q.Id,
                         ImpersonatorTenantId = q.ImpersonatorTenantId,
                         ImpersonatorUserId = q.ImpersonatorUserId,
                         MethodName = q.MethodName,
                         Name = a2.Name,
                         Parameters = q.Parameters,
                         ReturnValue = q.ReturnValue,
                         ServiceName = q.ServiceName,
                         TenantId = q.TenantId,
                         UserId = q.UserId,
                         UserName = a2.UserName
                     };
            filter.SetDefaultSortIfNot("Id");
            return await this.CreatePagedResultAsync(qr, filter);
        }

    }
}


